本篇的「岔題」在開頭。撰寫鐵人賽系列文章時,我的目標不是完整介紹 Modern C++ 的每一個方面,而是把基本功能與用法,透過簡單的範例,做個粗淺的介紹。C++ 畢竟是資深系統語言,即使範圍限縮在 Modern C++ 完整介紹需要投入相當規模的篇幅。若讀者發現發佈於鐵人賽系列文章只講到皮毛,屬正常現象。
初學 std::shared_ptr<T>
常見幾個疑問:
c<T>
,那麼應該設計成 Call by Value, Call by Reference, Call by Pointer?std::shared_ptr<T>
是不是 thread-safe?Microsoft 過去一年在 C++ 的文件上做了蠻大幅度的更新,關於 std::shared_ptr
的使用,可以參考這篇文件。
「Modern C++」有一個寬鬆的要求:盡可能不要直接使用 new/delete 來建構物件或使用記憶體。遇到 new/delete 應該改用 std::make_unique
或者 std::make_shared
來取代。《Effective Modern C++》Item 21 有說明理由。
第二個理由是為了避免 Memory Leak。考慮以下程式碼:
void Admit1Country2Systems(std::shared_ptr<TeaShopOwner>,
bool yes_or_no);
Admit1Country2Systems(std::shared_ptr<TeaShopOwner>(new TeaShopOwner),
SayYes());
在 C++17 之前,上述函數呼叫可能出現 Memory Leak,原因是 C++11⁄14 標準規格中,沒有規定函數的參數被處理的順序,因此上述的函數呼叫,有可能發生 new TeaShopOwner
完成,但還沒有被放進 std::shared_ptr
管理,結果 SayYes()
裡拋出異常,因此導致 Memory Leak(new TeaShopOwner
沒有刪除)。
由於以上原因,Modern C++ 最佳實務是「對自己好一點」,盡可能使用 std::make_shared
來創建物件。
前述例子中的函數將 std::shared_ptr
參數定義為 By Value。「語義」是:呼叫此函數會將傳入的 std::shared_ptr
物件的「參用計數」加一。如此一來,即使呼叫該函數為非同步操作,也不用擔心 std::shared_ptr
管理的物件被提前刪除。
void Admit1Country2Systems(std::shared_ptr<TeaShopOwner>,
bool yes_or_no);
關於這個問題,多年前多位 C++ 大神曾經回答過,有興趣的讀者可以觀看這支影片。
在場的幾位重量級大神(包含 C++ 之父)針對這個問題也有不同的意見。就現場激起的討論中,我們得到一個結論:C++ 真是一個複雜的程式語言吶。
看了好幾次才看懂這句話的意思:「前述例子中的函數將 std::shared_ptr 參數定義為 By Value。「語義」是:呼叫此函數會將傳入的 std::shared_ptr 物件的「參用計數」加一。」
汗顏。系列文章還有很多待加強之處,還請多包涵。